package com.km.team.phsystem.service.impl.base;

import com.km.team.phsystem.beans.vo.BaseQueryVo;
import com.km.team.phsystem.common.enums.ResultEnum;
import com.km.team.phsystem.common.exception.BaseServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;

/**
 * Author: jiangjun
 * Date: 2018/9/11
 * Time: 下午2:26
 * Description: 服务基类。
 * 1：默认创建各个子类服务logger对象
 * 2：提供单表基础的insert、update、delete等通用操作
 * 3：T：子类服务接口类型 M：model类型
 */
@Component
public class BaseService<T, M> implements ApplicationContextAware {

    protected Logger logger = null;

    private ApplicationContext applicationContext;

    public BaseService() {
        try {
            if (this.getClass().getGenericSuperclass().getTypeName().equals(Object.class.getName())) {
                return;
            }
            ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
            Class<T> clazz = (Class<T>) pt.getActualTypeArguments()[0];
            logger = LoggerFactory.getLogger(clazz);
        } catch (Exception e) {
            throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(),
                    ResultEnum.INTERNAL_ERROR.getMessage(),
                    "^_^ BaseService get sub class error ^_^", e);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 计算startIndex
     * @param vo
     * @return
     */
    public BaseQueryVo calculateStartIndex(BaseQueryVo vo){
        int startIndex = (vo.getPageBean().getCurrentPage() - 1)
                * vo.getPageBean().getPageSize();
        vo.getPageBean().setStartIndex(startIndex);
        return vo;
    }

    /**
     * 通用获取单表数据
     *
     * @param id
     * @return
     */
    public M baseGet(Integer id) {
        if (id == null) {
            return null;
        }
        try {
            // 获取model类型、mapper接口和select方法
            Object mapper = getMapper();
            Method getMethod = mapper.getClass().getMethod(MethodConstants.SELECT_BY_PRIMARY_KEY, Integer.class);
            M model = (M) getMethod.invoke(mapper, id);
            return model;
        }catch (Exception e) {
            throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(),
                    "^_^ BaseService invoke mapper's select method error ^_^", e);
        }
    }

    /**
     * 通用更新（insert/update）单表数据
     * @param record
     * @return
     */
    @Transactional
    public int baseUpdate(M record){
        if(record == null){
            return 0;
        }
        try {
            Method getIdMethod = record.getClass().getMethod(ModelConstants.GET_ID);
            if(getIdMethod == null){
                String extraMsg = String.format("BaseService not get model's getId method！model class: {}",
                        record.getClass());
                throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(),
                        ResultEnum.INTERNAL_ERROR.getMessage(),
                        extraMsg);
            }
            Integer id = (Integer) getIdMethod.invoke(record);
            // 获取model类型、mapper接口
            Object mapper = getMapper();
            Method invokeMethod = null;
            if(id == null){
                // insert
                // 获取insert方法
                invokeMethod = mapper.getClass().getMethod(MethodConstants.INSERT_SELECTIVE, record.getClass());
            }else {
                // update
                // 获取insert方法
                invokeMethod = mapper.getClass().getMethod(MethodConstants.UPDATE_BY_PRIMARY_KEY_SELECTIVE, record.getClass());
            }
            int rows = (int) invokeMethod.invoke(mapper, record);
            return rows;
        }catch (Exception e) {
            throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(),
                    "^_^ BaseService invoke mapper's insert or update method error ^_^", e);
        }
    }

    /**
     * 通用删除（更新状态为删除/失效）单表数据
     * @param id
     * @return
     */
    @Transactional
    public int baseDelete(Integer id){
        if(id == null){
            return 0;
        }
        try {
            // 获取model类型、mapper接口和select方法
            Object mapper = getMapper();
            Object deleteRecord = initDeleteRecord(id);
            // get update method
            Method updateMethod = mapper.getClass().getMethod(MethodConstants.UPDATE_BY_PRIMARY_KEY_SELECTIVE, deleteRecord.getClass());
            // update
            int rows = (int) updateMethod.invoke(mapper, deleteRecord);
            return rows;
        }catch (Exception e) {
            throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(),
                    "^_^ BaseService invoke mapper's update method error ^_^", e);
        }

    }

    /**
     * 初始化删除数据
     * @param id
     * @return
     * @throws Exception
     */
    private Object initDeleteRecord(Integer id) throws Exception{
        Class modelClass = getModelClass();

        Object record = modelClass.newInstance();
        // set id
        Method setIdMethod = modelClass.getMethod(ModelConstants.SET_ID, Integer.class);
        setIdMethod.invoke(record, id);
        // set data_status
        Method setDataStatusMethod = modelClass.getMethod(ModelConstants.SET_DATA_STATUS, Integer.class);
        setDataStatusMethod.invoke(record, ModelConstants.DELETE_DATA_STATUS);
        return record;
    }

    /**
     * 获取model类型
     *
     * @return
     */
    private Class getModelClass() {
        try {
            ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
            Class<T> modelClazz = (Class<T>) pt.getActualTypeArguments()[1];
            if (modelClazz == null) {
                String extraMsg = String.format("BaseService not get model class！model class: {}",
                        modelClazz.getName());
                throw new BaseServiceException(ResultEnum.INTERNAL_ERROR.getCode(),
                        ResultEnum.INTERNAL_ERROR.getMessage(),
                        extraMsg);
            }
            return modelClazz;
        } catch (Exception e) {
            logger.error("^_^ BaseService get model class error ^_^", e);
            throw e;
        }
    }

    /**
     * 获取model对象的mapper接口
     *
     * @return
     */
    private Object getMapper() {
        Class<T> modelClazz = getModelClass();
        String mapperName = genMapperBeanName(modelClazz.getSimpleName());
        Object mapper = applicationContext.getBean(mapperName);
        return mapper;
    }

    /**
     * generator mapper bean name
     *
     * @param modelSimpleName
     * @return
     */
    private String genMapperBeanName(String modelSimpleName) {
        String originName = modelSimpleName.concat(MethodConstants.MAPPER_BEAN_SUFFIX);
        String mapperName = originName.substring(0, 1).toLowerCase()
                .concat(originName.substring(1));
        return mapperName;
    }
}
